package com.ee.bruscar;

import java.io.File;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import com.ee.bruscar.configuration.InterfaceConfiguration;
import com.ee.bruscar.configuration.hint.Hint;
import com.ee.bruscar.configuration.hint.ListModelHint;
import com.ee.bruscar.configuration.hint.MethodHint;
import com.ee.bruscar.configuration.hint.ModelHint;
import com.ee.bruscar.configuration.hint.lookup.HintLookup;
import com.ee.bruscar.configuration.maker.DefaultListMaker;
import com.ee.bruscar.configuration.maker.DefaultMaker;
import com.ee.bruscar.configuration.maker.HintedListMaker;
import com.ee.bruscar.configuration.maker.HintedModelMaker;
import com.ee.bruscar.configuration.maker.Maker;
import com.ee.bruscar.configuration.maker.StringMaker;
import com.ee.bruscar.configuration.parser.InterfaceConfigXmlParser;
import com.ee.bruscar.exceptions.BruscarException;
import com.ee.bruscar.util.reflection.TypeChecker;

public class Bruscar
{
	private InterfaceConfiguration config;
	
	public Bruscar(File configFile)
	{
		InterfaceConfigXmlParser parser = new InterfaceConfigXmlParser();
		this.config = parser.parse( configFile );
	}

	public Object getStubDataFor(String methodName)
	{
		Maker maker = getMakerFor(methodName);
		return maker.make();
	}
	
	private Maker getMakerFor(String methodName)
	{
		Hint hint = config.getHintFor( methodName );
		
		if( TypeChecker.isList( getReturnType(methodName) ) )
		{
			return getListMakerFor( methodName );
		}
		if( hint == null )
		{
			return new DefaultMaker( getReturnType( methodName ) );
		}
		if( hint instanceof MethodHint )
		{
			return new StringMaker( hint );
		}
		if( hint instanceof ModelHint )
		{
			return new HintedModelMaker( (ModelHint) hint, config );
		}
		
		throw new BruscarException("can't create maker for " + methodName );
	}
	
	private Maker getListMakerFor(String methodName)
	{
		Hint hint = config.getHintFor( methodName );
		
		if( hint == null )
		{
			return new DefaultListMaker( getListReturnType(methodName) );
		}
		
		return new HintedListMaker( (ModelHint) hint, config );
	}

	private Class<?> getListReturnType(String methodName)
	{
		try
		{
			Class<?> clazz = Class.forName( config.getInterfaceToHonour() );
			Method[] methods = clazz.getMethods();
			
			for( int i = 0 ; i < methods.length ; i++ )
			{
				Method method = methods[i];
				
				if( method.getName().equals( methodName))
				{
					Type type = method.getGenericReturnType();
					if ( type instanceof ParameterizedType) 
					{  
						ParameterizedType pt = (ParameterizedType) type;  
							
						for (Type t : pt.getActualTypeArguments()) 
						{  
							return getClass( t );
						}  
					}
				}
			
			}
		}
		catch (ClassNotFoundException e)
		{
			e.printStackTrace();
		}
		
		return null;
	}
	
	public static Class<?> getClass(Type type) 
	{
		if (type instanceof Class) 
		{
			return (Class) type;
		}
		else if (type instanceof ParameterizedType) 
		{
			return getClass(((ParameterizedType) type).getRawType());
		}
		else if (type instanceof GenericArrayType) 
		{
			Type componentType = ((GenericArrayType) type).getGenericComponentType();
			Class<?> componentClass = getClass(componentType);
			if (componentClass != null ) 
			{
				return Array.newInstance(componentClass, 0).getClass();
			}
			else 
			{
				return null;
			}
		}
		else 
		{
			return null;
		}
	}

	private Class<?> getReturnType(String methodName)
	{
		try
		{
			Class<?> clazz = Class.forName( config.getInterfaceToHonour() );
			//?? Method method = clazz.getDeclaredMethod( methodName, parameterTypes)
			Method[] methods = clazz.getMethods();
			for( int i = 0 ; i < methods.length ; i++ )
			{
				Method method = methods[i];
				
				if( method.getName().equals( methodName))
				{
					return method.getReturnType();
				}
			}
			
			throw new BruscarException("couldn't find returntype for : " + methodName );
		}
		catch (Exception e)
		{
			throw new BruscarException( "Error checking isComplexReturnType", e );
		}
	}


}
